﻿using Leo.Common.MQ;
using Microsoft.Extensions.DependencyInjection;

namespace Leo.Common.RabbitMQ
{ 

    public class RabbitQueueInfo : RabbitObjectBase
    {

        public RabbitQueueInfo PreventDefaultConsumer()
        {
            this.preventDefaultConsumer = true;
            return this;
        }

        private const ushort DEFAULT_QOS = 1;

        internal readonly List<HandlerDescriptor> handlerTypes = new List<HandlerDescriptor>();

        internal bool preventDefaultConsumer;

        public RabbitQueueInfo(string name) : this(name, false, true, false, 1)
        {
        }

        public RabbitQueueInfo(string name, bool exclusive, bool durable, bool autoDelete) : this(name, exclusive, durable, autoDelete, 1)
        {
        }

        public RabbitQueueInfo(string name, bool exclusive, bool durable, bool autoDelete, ushort qos) : base(name)
        {
            this.Binders = new List<RabbitQueueBinder>();
            this.Exclusive = exclusive;
            this.Qos = ((qos <= 1) ? 1 : qos);
            base.Durable = durable;
            base.AutoDelete = autoDelete;
            if (exclusive)
            {
                this.GenerateExclusiveQueueName();
            }
        }

        public bool Exclusive { get; set; }

        public ushort Qos { get; set; } = 1;

        //[Nullable(2)]
        public RabbitQueueBinder PreferBinder { /*[NullableContext(2)]*/ get;/* [NullableContext(2)]*/ set; }

        public List<RabbitQueueBinder> Binders { get; }

        //[Nullable(2)]
        public MessageRetryStrategy RetryStategy {/* [NullableContext(2)] */get; /*[NullableContext(2)] */private set; }

        //[Nullable(2)]
        public MessageRejectStrategy RejectStrategy { /*[NullableContext(2)] */get; /*[NullableContext(2)]*/ private set; }

        public RabbitQueueInfo AddBinder(RabbitQueueBinder binder)
        {
            if (binder == null)
            {
                throw new ArgumentNullException("binder");
            }
            if (!this.Binders.Contains(binder))
            {
                this.Binders.Add(binder);
            }
            return this;
        }

        public RabbitQueueInfo AddHandler</*[Nullable(0)] */T>() where T : IMessageHandler
        {
            return this.AddHandler(typeof(T), null);
        }

        public RabbitQueueInfo AddHandler</*[Nullable(0)]*/ T>(T handler) where T : IMessageHandler
        {
            return this.AddHandler(typeof(T), handler);
        }

        public RabbitQueueInfo AddHandler(Type type)
        {
            return this.AddHandler(type, null);
        }

        internal override bool Declare(RabbitConnection connection)
        {
            connection.Declare(this);
            return true;
        }

        private RabbitQueueInfo AddHandler(Type type, /*[Nullable(2)]*/ object instance)
        {
            HandlerDescriptor handlerDescriptor = new HandlerDescriptor(type, instance);
            if (this.handlerTypes.Contains(handlerDescriptor))
            {
                //throw new JaredsException("不能重复添加handler");
                throw new Exception("不能重复添加handler");
            }
            this.handlerTypes.Add(handlerDescriptor);
            return this;
        }
         
        public void ClearHandlers()
        {
            this.handlerTypes.Clear();
        }
         

        public RabbitQueueInfo EnableRetryStrategy(TimeSpan retryInterval, int retryTimes,/* [Nullable(2)]*/ string destination = null)
        {
            this.RetryStategy = new MessageRetryStrategy(retryInterval, retryTimes, this, false, destination);
            return this;
        }
         
        public RabbitQueueInfo EnableRetryStrategy(TimeSpan retryInterval, int retryTimes, bool temporary, /*[Nullable(2)]*/ string destination = null)
        {
            this.RetryStategy = new MessageRetryStrategy(retryInterval, retryTimes, this, temporary, destination);
            return this;
        }
         
        public RabbitQueueInfo EnableDeadStrategy(/*[Nullable(2)] */string destination = null)
        {
            return this.EnableDeadStrategy(false, destination);
        }
         
        public RabbitQueueInfo EnableDeadStrategy(bool temporary, /*[Nullable(2)]*/ string destination = null)
        {
            this.RejectStrategy = new MessageRejectStrategy(destination, this, temporary);
            return this;
        }
         
        public RabbitQueueInfo BindTo(RabbitExchangeInfo exchange, string routingKey)
        {
            exchange.redeclared = (exchange.redeclared || this.redeclared);
            RabbitQueueBinder rabbitQueueBinder = new RabbitQueueBinder(exchange, this, routingKey);
            this.AddBinder(rabbitQueueBinder);
            if (this.PreferBinder == null)
            {
                this.PreferBinder = rabbitQueueBinder;
            }
            return this;
        }
         
        public RabbitQueueInfo PreferBind(RabbitExchangeInfo exchange, string routingKey)
        {
            this.BindTo(exchange, routingKey);
            this.PreferBinder = this.Binders.Find((RabbitQueueBinder x) => x.Queue.Name == this.Name && x.Exchange.Name == exchange.Name && x.RoutingKey == routingKey);
            return this;
        }
         
        public static RabbitQueueInfo Named(string name)
        {
            return new RabbitQueueInfo(name);
        }
         
        public RabbitQueueInfo AutoDeleted()
        {
            base.AutoDelete = true;
            return this;
        }
         
        public RabbitQueueInfo Exlusived()
        {
            this.Exclusive = true;
            this.GenerateExclusiveQueueName();
            return this;
        }
         
        public RabbitQueueInfo NoDurable()
        {
            base.Durable = false;
            return this;
        }
         
        public RabbitQueueInfo QualityOfService(ushort qos)
        {
            this.Qos = qos;
            return this;
        }
         
        public RabbitQueueInfo RedeclareWhenConflict()
        {
            this.redeclared = true;
            this.Binders.ForEach(delegate (RabbitQueueBinder v)
            {
                v.Exchange.redeclared = true;
            });
            return this;
        }
         
        private string GenerateExclusiveQueueName()
        {
            base.Name += Guid.NewGuid().ToString("N").Substring(0, 16);
            return base.Name;
        }
         
        public List<IMessageHandler> GetHandlerInstances(IServiceProvider service)
        {
            List<IMessageHandler> list = new List<IMessageHandler>();
            foreach (HandlerDescriptor handlerDescriptor in this.handlerTypes)
            {
                if (handlerDescriptor.HandlerInstance != null)
                {
                    list.Add((IMessageHandler)handlerDescriptor.HandlerInstance);
                }
                else
                {
                    IMessageHandler messageHandler = (IMessageHandler)ServiceProviderServiceExtensions.GetRequiredService(service, handlerDescriptor.HandlerType);
                    list.Add(messageHandler);
                }
            }
            return list;
        }
         
     
    }
}
